﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.Text.RegularExpressions;

namespace UpdateBot2
{
    class Version : IComparable<Version>
    {
        private List<int> _versionSlots = new List<int>(4);

        public ReadOnlyCollection<int> VersionSlots
        {
            get
            {
                return this._versionSlots.AsReadOnly();
            }
        }

        public int Count
        {
            get
            {
                return this._versionSlots.Count;
            }
        }

        public Version(String ver)
        {
            this.parseString(ver);
        }

        private void parseString(String ver)
        {
            //If empty string, just add 0.
            if (String.IsNullOrWhiteSpace(ver))
            {
                this._versionSlots.Add(0);
            }
            else
            {
                //Prep string, split by dots
                String prepVer = prepareVersionString(ver);
                String[] verParts = prepVer.Split('.');

                //Preload lengths
                int numVerParts = verParts.Length;

                //Iterate through each section
                for (int p = 0; p < numVerParts; p++)
                {
                    //Extract digits and assign to corresponding version slot of the class.
                    int digitsAsNum = extractDigits(verParts[p]);
                    this._versionSlots.Add(digitsAsNum);
                }
            }
        }

        /// <summary>
        /// Compares versions by version chunks.
        /// Returns: 0 on equal, less than 0 if this &lt; other, greater than 0 if this &gt; other.
        /// </summary>
        /// <param name="other">Version to compare to this instance.</param>
        /// <returns>0 on equal, less than 0 if this &lt; other, greater than 0 if this &gt; other.</returns>
        public int CompareTo(Version other)
        {
            Version v1 = this;
            Version v2 = other;

            //Choose largest count to scan from.
            int v1Count = v1.Count;
            int v2Count = v2.Count;
            int largestCount = (int)Math.Max(v1Count, v2Count);

            //Iterate, if index is maxed, value = 0.

            for (int v = 0; v < largestCount; v++)
            {
                //Apply if parameters to check indexes... if > v=0
                int version1 = (v < v1Count) ? v1.VersionSlots[v] : 0;
                int version2 = (v < v2Count) ? v2.VersionSlots[v] : 0;

                //If version numbers are not equal, return comparison!
                //Otherwise, let loop proceed to next version chunk!
                if (version1 != version2)
                {
                    return version1.CompareTo(version2);
                }
            }

            //If version loops through and all are equal, return 0.
            return 0;
        }

        public override string ToString()
        {
            StringBuilder bob = new StringBuilder();
            int numSlots = this._versionSlots.Count;

            //Iterate through each number; old for loops needed to see if index is last one.
            for (int i = 0; i < numSlots; i++)
            {
                bob.Append(this._versionSlots[i].ToString());

                //If you're not the last number, append a dot!
                if (i < numSlots - 1)
                {
                    bob.Append('.');
                }
            }

            return bob.ToString();
        }

        /// <summary>
        /// This method is used to prepare version strings to be parsed.
        /// Current function: trims and prepends a 0 if string begins with a decimal.
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static String prepareVersionString(String s)
        {
            //Check is there are any dots.
            s = s.Trim();
            int firstDotPos = s.IndexOf('.');

            if (firstDotPos < 0)
            {
                //There are no dots! Append a .0 and call it good enough!
                return s + ".0";
            }
            else if (firstDotPos == 0)
            {
                //dot is first character! Prepend 0!
                return '0' + s;
            }
            else
            {
                //Eh, should be just fine ;)
                return s;
            }
        }

        /// <summary>
        /// Extracts all digits from the given string.
        /// </summary>
        /// <param name="verChunk">The string to be parsed.</param>
        /// <returns>Integer based on digits in the given string; 0 on failure or invalid number.</returns>
        private static int extractDigits(String verChunk)
        {
            //Return 0 on empty string.
            if (verChunk.Length < 1)
            {
                return 0;
            }

            String result = String.Empty;

            //Loop through each character and extract digits.
            foreach (char c in verChunk)
            {
                if (Char.IsNumber(c))
                {
                    result += c;
                }
            }

            //If no digits found, return 0!
            if (result.Length < 1)
            {
                return 0;
            }
            else
            {
                //Try to parse result; should be valid number... however, caution is best.
                int num = 0;
                int.TryParse(result, out num);
                return num;
            }
        }

        /// <summary>
        /// Extracts a version string from the given string.
        /// </summary>
        /// <param name="text">The raw text to examine.</param>
        /// <returns>Version string if present.</returns>
        public static Version ExtractVersionFromString(String text)
        {
            Regex reg = new Regex(@"([0-9]+\.)+([0-9]+)");
            return new Version(reg.Match(text).Value);
        }
    }
}
